#include <stdio.h>
#include <time.h>		// clock()
#include <iostream>
using namespace std;

//#include <gl/gl.h>
//#include <gl/glu.h>
#include "glut.h"
#include "CGfxOpenGL.h"
#include "Cell.h"
#include "drawing.h"
#include "circle.h"
#include "line.h"
#include "Map.h"
#include "Piece.h"
#include "Player.h"
#include "al.h"
#include "alc.h"
#include "alut.h"
#define SCREEN_WIDTH		800
#define SCREEN_HEIGHT		600 //I just realized this was misspelled. Changing.
#define SCREEN_POSITION_X	10
#define SCREEN_POSITION_Y	50
#define FIRST_LEVEL_RED		255
#define FIRST_LEVEL_GREEN	255
#define FIRST_LEVEL_BLUE	0
#define MAP_HEIGHT			13
#define MAP_WIDTH			7
#define NUMBER_OF_PLAYERS	4
#define NUMBER_OF_PIECES	18
#define PIECE_LIMIT			9
#define MINUS_WORLD_GLITCH	false


//We can declare all the functions up here.
int totalSelected(void);
void movePiece(V2DF);
bool setUp(V2DF);
bool checkForNextFloor(void);
void goToNextFloor(void);

//Bad global variables! But it's necessary.
CGfxOpenGL *Menu_Screen = NULL;
Map map;
Player players[NUMBER_OF_PLAYERS];	//A static array right now, it won't be difficult to change it to a dynamic later.
bool skip = false;					//This will tell the computer whether or not that person pressed spacebar (the skip button)
int currentPlayer = 0; //This is the current player, and is used to tell WHO'S turn it currently is, based on Red's turn, Green's turn, etc.
bool menuOn = true;
int totalPlayers = 4;
int windowID;
bool altrue = true; //I hate you global variables, but I don't know how to
ALuint Source;

ALuint piece_moveSoundSource; //Damn you global variables! Until there's a better way around these, we have to use them.
/*  
This function converts screen coordinates into OpenGL coordinates 
http://nehe.gamedev.net/data/articles/article.asp?article=13
*/

bool checkForNextFloor(void) //This function will check to see if ALL SURVIVAL SPOTS *even the one on the fourth* has been taken.
{
		for(int row = 0; row < MAP_HEIGHT; row++)
		{
			for(int col = 0; col < MAP_WIDTH; col++)
			{
				if(map.getCell(row, col).getSurvive() && map.getCell(row, col).drawned()) //This means it's a survival spot.
				{
					//If it's NOT taken, return false. Get out of this as early as possible for optimization.
					if(!map.getCell(row, col).getTaken())
					{
						return false;
					}
				} //If it gets here, it means that it HAS been taken.
			}
		}

	//If it gets all the way down here, it means that ALL THE SURVIVAL SPOTS HAVE BEEN TAKEN. Return true.
	return true;
}

void goToNextFloor(void)
{
	//This will erase all the pieces that ARE NOT ON survival spots. Ouch. Luckily, this will hopefully work as planned. T_T
	for(int row = 0; row < MAP_HEIGHT; row++)
	{
		for(int col = 0; col < MAP_WIDTH; col++)
		{
			if(!map.getCell(row, col).getSurvive() && map.getCell(row, col).drawned())
			{
				if(map.getCell(row, col).getTaken())
				{
					//The spot is NOT a survival spot, and currently IS being taken by a player.
					for(int currentPlayer = 0; currentPlayer < totalPlayers; currentPlayer++)
					{
						int lazy = players[currentPlayer].findPiece(map.getCell(row, col).getX(), map.getCell(row, col).getY());

						if((players[currentPlayer].findPiece(lazy)->getX() == map.getCell(row, col).getX()) && 
							players[currentPlayer].findPiece(lazy)->getY() == map.getCell(row, col).getY())
						{
							//THIS player has a piece on that cell. Get rid of it!
							players[currentPlayer].findPiece(lazy)->setDraw(false);
							map.getCell(row, col).unTake();

							break;
						}
					}
				}
			}
		}
	}
}

bool piecesLeft()
{
	//This function will use the global variable currentPlayer, as well as the global variables of the players.
	for(int i = 0; i < players[currentPlayer].getNumber(); i++)
	{
		Piece * p = players[currentPlayer].findPiece(i);
		if(p->getDraw())
		{
			//Meaning that at the least ONE PIECE is still being drawn.
			return true;
		}
	}
	return false;
}

bool setUp(V2DF click)
{
	bool setUp = false;
	static int totalPieces = 0; //Again, this will be set to zero. I'm pretty sure.
	static int currentPiece = 0;

	for(int row = 0; row < MAP_HEIGHT; row++)
	{
		for(int col = 0; col < MAP_WIDTH; col++)
		{
			if(map.getCell(row, col).getPosition().isWithinRadius(click) && map.getCell(row, col).drawned()) //They clicked inside a cell.
			{
				if(	((col == 3) && (row == 0 || row == 12)) ||
					((col == 2 || col == 4) && (row == 1 || row == 11)) ||
					((col == 1 || col == 5) && (row == 2 || row == 10)) ||
					((col == 0 || col == 6) && (row == 3 || row == 5 || row == 7 || row == 9)))
				{
					//yay more if statements!
					if(map.getCell(row, col).getTaken() == false) //It's not taken yet! Eligible!
					{
//						printf("%d", players[0].findPiece(0).getDraw());

						//Debug code. once this works, get rid of this comment and the next few lines.
						//printf("Click X: %f, Click Y: %f \n", click.getX(), click.getY());

						//Until here, actually.
						players[currentPlayer].findPiece(currentPiece)->move(map.getCell(row, col).getX(), map.getCell(row, col).getY()); //Can anybody else figure this out and WHY IT WON'T WORK!?!?!?!!?!?!?!?!?!?!?11!!
						//printf("Piece X: %f, Piece Y: %f \n", players[currentPlayer].findPiece(currentPiece)->getX(),players[currentPlayer].findPiece(currentPiece)->getY());
//						printf("%d", players[0].findPiece(0).getDraw());

						currentPlayer += 1;
						map.getCell(row, col).take();
						//printf("Eligible.\n");
						totalPieces += 1;
					}

					else //Oh, it IS taken! Choose another spot!
					{
						//printf("Not Eligible.\n");
					}
				}
			}
		}
	}

	if(currentPlayer >= totalPlayers)
	{
		currentPiece += 1; //Moves to the next piece.
		currentPlayer = 0; //Goes back to the first person.
	}

	if(totalPieces >= NUMBER_OF_PIECES)
	{
		setUp = true;
		printf("Done with Setup!\n");
		currentPlayer = 0;
	}
	if(totalPlayers == 4) //If it's four player, then that means that TWO SPACES are left unused.
	{
		if(totalPieces >= 16)
		{
			setUp = true;
			printf("Done with Setup!\n");
			currentPlayer = 0;
		}
	}

	return setUp;
}

void movePiece(V2DF click)
{
	if(skip)
	{
		skip = false;
	}


	Player * p = &players[currentPlayer];
	for(int row = 0; row < MAP_HEIGHT; row++)
	{
		for(int col = 0; col < MAP_WIDTH; col++)
		{
			if(map.getCell(row, col).getPosition().isWithinRadius(click) && map.getCell(row, col).drawned())
			{
				static int lastRow = 0;
				static int lastCol = 0;
				//They've clicked on a circle. Now for some more logic.
				//Have they clicked on a piece?
				bool blah = map.getCell(row, col).getTaken();
				if(blah) //This will let the computer know if that cell has a piece.
				{
					bool selected = false;
					int tooLazy = 0;

					for(int i = 0; i < p->getNumber(); i++)
					{
						if(p->findPiece(i)->getSelect())
						{
							selected = true;
							tooLazy = i;
							break;
						}
					}

					if(!selected)
					{
						//If so, is the player color the same as the piece's owner?
						int currentPiece = players[currentPlayer].findPiece(map.getCell(row, col).getX(), map.getCell(row, col).getY());
						Piece * piece = p->findPiece(currentPiece);

						if(piece->getColor() == p->getColor()) //Is the current piece's color the same as the player's?
						{
							//Yes, apparently. Note that I can already forsee an error here: It will be VERY difficult to get the exact same points. Find some way around this.
							piece->select();
							if((p->getColor() == currentPlayer) && (p->getColor() == piece->getColor()))
							{
								//Current glitch is RIGHT HERE. This is the ONLY call of "nextPosition".
								map.deSelect();
								map.nextPosition(row, col);
								//The fact it didn't catch an error here (there was a semicolon next to the if statement) worries me.
								//printf("Next. %d, %d, %d", p->getColor(), piece->getColor(), currentPlayer);
							}
							lastRow = row;
							lastCol = col;
							//players[currentPlayer].findPiece(players[currentPlayer].findPiece(map.getCell(row, col).getX(), map.getCell(row, col).getY()))->showNextPossibleMoves(map.getCell(row, col), map);
							//Has it already been selected?
						}
						else
						{
							printf("Wrong piece.\n");
						//	printf("Next. %d, %d, %d", p->getColor(), piece->getColor(), currentPlayer);
							//The second one NEVER changes for some reason: it's ALWAYS zero: which is how it gets away with it.
							//The glitch that causes it is due to piece->getColor() NOT getting the correct color! Now to figure out how to fix it.
						}
					}
				}

				else //It the cell they clicked on DOES NOT have a piece on it.
				{
					//Is there a piece already selected?
					bool selected = false;
					int tooLazy = 0;

					for(int i = 0; i < p->getNumber(); i++)
					{
						if(p->findPiece(i)->getSelect())
						{
							selected = true;
							tooLazy = i;
							break;
						}
					}

					//If yes?
					if(selected) //If there is a piece that has already been selected:
					{
						if(map.getCell(row, col).posNext()) //Have they clicked on a viable spot?
						{
							//Then move that piece to the clicked position.
							int x, y;
							x = p->findPiece(tooLazy)->getX();
							y = p->findPiece(tooLazy)->getY();
							map.getCell(lastRow, lastCol).unTake(); //Untake the Piece's last position. This may be the cause of the "dead spot" glitches.
							//players[currentPlayer].findPiece(tooLazy)->showNextPossibleMoves(map.getCell(players[currentPlayer].findPiece(tooLazy)->getX(), players[currentPlayer].findPiece(tooLazy)->getY()), map);
						//	printf("%d", map.getCell(x, y).getTaken());
							
							//map.nextPosition(lastRow, lastCol); //This is where I'm pretty sure the program fucks up big time.
							
							map.deSelect();
							//In an attempt to make sure that it deselects all the current cells.
							p->findPiece(tooLazy)->move(map.getCell(row, col).getX(), map.getCell(row, col).getY()); //Move that piece to where the user clicked.

							if(altrue) //Make it easy enough to just mute the game.
								alSourcePlay(piece_moveSoundSource);


							p->findPiece(tooLazy)->deSelect(); //De select the piece. I should probably make this it's own function.
							map.getCell(row, col).take(); //Make sure the new spot has been taken, so other pieces can't go on top of it.

							if(!(row == 6 && col == 3) || map.getLevel() == 4) //THE CENTER. Wow I feel retarded now. I forgot to add the map.getLevel == 4, so they could never win!
							{
								currentPlayer += 1;

								if(currentPlayer >= totalPlayers)
								{
									currentPlayer = 0;
								}
								if(checkForNextFloor())
								{
									if(map.getLevel() != 4 || MINUS_WORLD_GLITCH)
									{
										goToNextFloor();
										map.changeLevel();
									}
									else
									{
										if(currentPlayer != 0) //Meaning that it was NOT the last player's turn.
											cout << "Player " << currentPlayer << " wins!" << endl;
										else
											cout << "Player " << totalPlayers << " wins!" << endl; //Yeah. The person that was last.
										glutDestroyWindow(windowID);//I need to find SOME way to completely exit out of the program. Any way will do.
										exit(0);
										//WHOO NO CRASHES! WHOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
									}
								}
							//Here's where I can check to see if the currentPlayer still has pieces remaining.
								while(!piecesLeft())
								{
									currentPlayer += 1;
									if(currentPlayer >= totalPlayers)
										currentPlayer = 0;
								}
							} //Well...That explains that glitch >_<;;; Wow, I feel retarded now.
						}
					}
				}
			}
		}
	}
}

V2DF GetOGLPos(int x, int y)
{
	GLint viewport[4];			// Where The Viewport Values Will Be Stored
	GLdouble modelview[16];		// Where The 16 Doubles Of The Modelview Matrix Are To Be Stored
	GLdouble projection[16];	// Where The 16 Doubles Of The Projection Matrix Are To Be Stored
	GLfloat winX, winY, winZ;	// Holds Our X, Y and Z Coordinates
	GLdouble posX, posY, posZ;	// Hold The Final Values

	glGetDoublev( GL_MODELVIEW_MATRIX, modelview );		// Retrieve The Modelview Matrix	
	glGetDoublev( GL_PROJECTION_MATRIX, projection );	// Retrieve The Projection Matrix
	glGetIntegerv( GL_VIEWPORT, viewport );				// Retrieves The Viewport Values (X, Y, Width, Height)

	winX = (float)x;						// Holds The Mouse X Coordinate
	winY = (float)viewport[3] - (float)y;	// Subtract The Current Mouse Y Coordinate From The Screen Height.
	glReadPixels( x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
	gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);

	return V2DF(posX, posY);
}

// @param x/y the coordinate of where the mouse is

void passiveMotion(int x, int y)
{
	V2DF click(GetOGLPos(x,y));

		for(int row = 0; row < MAP_HEIGHT; ++row)
		{
			for(int col = 0; col < MAP_WIDTH; ++col)
			{
				if(map.getCell(row,col).getPosition().isWithinRadius(click) && map.getCell(row,col).drawned())
				{/*
					printf("INSIDE CIRCLE at index: %d, %d\n", row,col);
					printf("inside circle at: %f, %f\n", click.getX(), click.getY());*/
				}
			}
		}
}

int totalSelected(void)
{
	//Because everything I need is already global, all I need to do is return an integer.
	int totalSelected = 0;

		for(int i = 0; i < totalPlayers; i++)
		{
			Player *p = &players[i];

				for(int k = 0; k < PIECE_LIMIT; k++) //K is the number of pieces per player TOTAL. Yes, this actually matters. Make it a #define. NOW.
				{
					if(p->findPiece(k)->getSelect())
					{
						totalSelected++;
					}
				}
		}

	return totalSelected; //This will make sure that ONLY ONE IS SELECTED. SO NO MORE CHEATING, RED. (As it continues to cheat)
}
//I have an idea of how to do this. But dear god, I don't want to do it. It would need dozens of global variables (if only so I could initialize them outwards.)

void mouseFunction(int button, int state, int x, int y)
{
	//Whoo! I much like this mouseFunction much better. Although movePiece could use a bit of help...
	V2DF click(GetOGLPos(x, y));
	static bool setup = false; //This will automatically be set to false: Which is perfect. And thanks to being static.

	if(state == 0)
	{
		if(setup == false) //Meaning that NONE of the pieces have been set up yet. more static variables, here we come! 
		{
			setup = setUp(click);
		}

		else
		{
			movePiece(click);
		}
	}
}

void keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		
		case 'm': // Mute music
			
			altrue = !altrue;	
				if(altrue)
					alSourcePlay(Source);
				if(!altrue)
					alSourceStop(Source);
				break;

		case ' ': // skip turn

			if(!menuOn)
			{
				printf("Skipped.\n");
				for(int i = 0; i < players[currentPlayer].getNumber(); i++)
				{
					if(players[currentPlayer].findPiece(i)->getSelect())
					{
						players[currentPlayer].findPiece(i)->deSelect(); //This will turn it off, allowing the new player a chance.
						map.deSelect(); //Make sure they can't take any remaining places on the board!!!!!!
					}
				}
				currentPlayer += 1;
				if(currentPlayer >= totalPlayers)
				{
					currentPlayer = 0;
				}
				break;
			}
			else
				menuOn = false;
		default:	break;
	}

	glutPostRedisplay();
}

int g_then = clock();

void gameUpdate(int a_timeDelay)
{
	int now = clock(), passed = now - g_then;

	g_then = now;

	glutPostRedisplay();
	glutTimerFunc(a_timeDelay, gameUpdate, a_timeDelay);
}
/** the display method for our GLUT program */
void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//Clear the screen and the Depth Buffer
//	glColor3ub(FIRST_LEVEL_RED, FIRST_LEVEL_GREEN, FIRST_LEVEL_BLUE);		// explicit about the color
	glLoadIdentity();

	if(menuOn)
	{
		//Leseamus' menu code.
	//	Menu_Screen->SetupProjection(SCREEN_WIDTH, SCREEN_HEIGHT); //Hey leseamus, I found your problem. Camera was in the wrong position.
		//But how do we bring it BACK!?
		Menu_Screen->Render();
	}
	else
	{
		// draw shapes
		map.glDraw();

			for(int i = 0; i < totalPlayers; i++)
			{
				players[i].glDraw();
			}

		players[currentPlayer].drawPiece(); //Draws the other rectangle on the far upper left hand corner, showing whose turn it is.
	}
	glutSwapBuffers();	// swap the draw buffers
	glFlush();
}
/*
	@param argc count of arguments passed into the program
	@param argv values of arguments passed into the program
*/

//To get this to work again, comment out from THIS line to...
ALuint Buffer;


ALuint piece_moveSoundBuffer;
//ALuint piece_moveSoundSource;

ALfloat SourcePos[] = {0.0, 0.0, 0.0}; //Where the sound comes from.
ALfloat SourceVel[] = {0.0, 0.0, 0.0}; //I have no idea.

ALfloat ListenerPos[] = {0.0, 0.0, 0.0}; //Where the listener currently is.
ALfloat ListenerVel[] = {0.0, 0.0, 0.0}; //I have no idea.
ALfloat ListenerOri[] = {0.0, 0.0, -1.0, 0.0, 1.0, 0.0}; //The orientation of the listener.

ALboolean loadALData(void)
{
	ALenum format;
	ALsizei size;
	ALvoid* data;
	ALsizei freq;
	ALboolean loop;

	alGenBuffers(1, &Buffer);

	if(alGetError() != AL_NO_ERROR)
	{
		return AL_FALSE;
	}

	alutLoadWAVFile("skflyingbattery.wav", &format, &data, &size, &freq, &loop);
	alBufferData(Buffer, format, data, size, freq);
	alutUnloadWAV(format, data, size, freq);

	alGenSources(1, &Source);

	if(alGetError() != AL_NO_ERROR)
	{
		return AL_FALSE;
	}

	alSourcei (Source, AL_BUFFER, Buffer);
	alSourcef (Source, AL_PITCH, 1.0f);
	alSourcef (Source, AL_GAIN, 1.0f);
	alSourcefv(Source, AL_POSITION, SourcePos);
	alSourcefv(Source, AL_VELOCITY, SourceVel);
	alSourcei (Source, AL_LOOPING, AL_TRUE);

	if(alGetError() != AL_NO_ERROR)
	{
		return AL_FALSE;
	}

	alGenBuffers(1, &piece_moveSoundBuffer);

	if(alGetError() != AL_NO_ERROR)
	{
		return AL_FALSE;
	}

	alutLoadWAVFile("piece_move.wav", &format, &data, &size, &freq, &loop);
	alBufferData(piece_moveSoundBuffer, format, data, size, freq);
	alutUnloadWAV(format, data, size, freq);

	alGenSources(2, &piece_moveSoundSource);

	if(alGetError() != AL_NO_ERROR)
	{
		return AL_FALSE;
	}

	alSourcei (piece_moveSoundSource, AL_BUFFER, piece_moveSoundBuffer);
	alSourcef (piece_moveSoundSource, AL_PITCH, 1.0F);
	alSourcef (piece_moveSoundSource, AL_GAIN, 1.0f);
	alSourcefv(piece_moveSoundSource, AL_POSITION, SourcePos);
	alSourcefv(piece_moveSoundSource, AL_VELOCITY, SourceVel);
	alSourcei(piece_moveSoundSource, AL_LOOPING, loop);

	if(alGetError() == AL_NO_ERROR)
	{
		return AL_TRUE;
	}

	return AL_FALSE;
}

void SetListenerValues(void)
{
	alListenerfv(AL_POSITION, ListenerPos);
	alListenerfv(AL_VELOCITY, ListenerVel);
	alListenerfv(AL_ORIENTATION, ListenerOri);
}

void KillALData(void)
{
	alDeleteBuffers(1, &Buffer);
	alDeleteSources(1, &Source);
	alutExit();
}

int main(int argc, char ** argv)
{
	printf("How many players? 2, 3, or 4?\n");
	char pla;
	cin >> pla;
	
	while((pla != '2' && pla != '3' && pla != '4') || !cin)
	{
		printf("2, 3, or 4 players?\n");
		cin >> pla;
	}
	totalPlayers = pla - '0';

	//To get past the black screen (while I try to get the menu screen to work), just press space.
	int width, height ;
	height = 800;		// retrieve width and height
	width = 600;
	Menu_Screen = new CGfxOpenGL;
	Menu_Screen->SetupProjection(width, height);

	glutInit(&argc, argv);
	glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
	glutInitWindowPosition(SCREEN_POSITION_X, SCREEN_POSITION_Y);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

	//Hi. This is what we call: WE NEED AN INTEGER HERE. *Creates global variable due to me having ot use it elsewhere.
	windowID = glutCreateWindow("Inner Circle");

	if(!Menu_Screen->Init())
	{
		return -1; //Return an error!!!!!
	}
	//THe image is obviously there, in memory, somewhere.


	altrue = true;

	if(altrue)
	{
		alutInit(&argc, argv);
		alGetError();

		if(loadALData() == AL_FALSE)
		{
			return -1;
		}

		SetListenerValues();
	
		alSourcePlay(Source);
		atexit(KillALData);
	}

//	printf("Position: %d, %d", cell.getX(), cell.getY()); //This is test code to make sure the cell class can set and get stuff.

		for(int i = 0; i < totalPlayers; i++)
		{
			players[i].setColor(i);
			players[i].setNumber(PIECE_LIMIT);
			players[i].setPieces();
		}


	glutDisplayFunc(display);
	glutMouseFunc(mouseFunction);
	glutPassiveMotionFunc(passiveMotion);
	glutKeyboardFunc(keyboard);
	//1000 / FPS = millisecondWaitTime
	glutTimerFunc(0, gameUpdate, (1000/80));
	glutPostRedisplay();
	glutMainLoop();

	Menu_Screen->Shutdown();
	
	delete Menu_Screen;
	return 0;
}